#pragma once

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "log.hpp"

namespace ns_server
{
    static const uint16_t defaultport = 8081;
    static const int backlog = 32; // 意思后面说

    using func_t = std::function<std::string(const std::string &)>;

    class TcpServer; // 声明
    class ThreadData
    {
    public:
        ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *ts)
            : sock(fd), clientip(ip), clientport(port), current(ts)
        {
        }

    public:
        int sock;
        std::string clientip;
        uint16_t clientport;
        TcpServer *current; //
    };

    class TcpServer
    {
    public:
        TcpServer(func_t func, uint16_t port = defaultport) : func_(func), port_(port), quit_(true)
        {
        }
        void initServer()
        {
            // 1.创建socket ，文件
            listensock_ = socket(AF_INET, SOCK_STREAM, 0);
            if (listensock_ < 0)
            {
                // std::cerr << "create socket error" << std::endl;
                logMessage(Fatal, "create socket error, code: %d, error string: %s", errno, strerror(errno));
                exit(SOCKET_ERR);
            }

            logMessage(Info, "create socket success, code: %d, error string: %s", errno, strerror(errno));

            // 2.绑定
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(port_);
            local.sin_addr.s_addr = INADDR_ANY; // 是全0,也可以写成htonl(INADDR_ANY)
            if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                // std::cerr << "bind socket error " << std::endl;
                logMessage(Fatal, "bind socket error, code: %d, error string: %s", errno, strerror(errno));
                exit(BIND_ERR);
            }

            logMessage(Info, "bind socket success, code: %d, error string: %s", errno, strerror(errno));

            // 3.监听
            if (listen(listensock_, backlog) < 0)
            {
                // std::cerr << "listen socket error " << std::endl;
                logMessage(Fatal, "listen socket error, code: %d, error string: %s", errno, strerror(errno));
                exit(LISTEN_ERR);
            }

            logMessage(Info, "listen socket success, code: %d, error string: %s", errno, strerror(errno));
        }
        void start()
        {
            // signal(SIGCHLD,SIG_IGN);//对信号进行忽略，忽略后，就不需要等了，不用写waitpid推荐

            // signal(SIGCHLD,handler);//以回收的方式，不推进

            quit_ = false;
            while (true)
            {
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                // 4.获取连接，accept
                int sock = accept(listensock_, (struct sockaddr *)&client, &len);
                if (sock < 0)
                {
                    // std::cout << "accept error" << std::endl;
                    logMessage(Warning, "accept error, code: %d, error string: %s", errno, strerror(errno));
                    continue;
                }

                // 提取client信息
                std::string clientip = inet_ntoa(client.sin_addr); // client的ip
                uint16_t clientport = ntohs(client.sin_port);      // 从网络中来的，所以要转出网络序列,client的port

                // 5.获取新连接成功,就开始进行业务处理
                // std::cout << "获取新连接成功: " << sock << " from " << listensock_ << " , "
                //           << clientip << " - " << clientport << std::endl; // 文件描述符sock from listensock,打印出来
                logMessage(Info, "获取新连接成功：%d form %d,who: %s - %d", sock, listensock_, clientip.c_str(), clientport);

                // v1版，测试版
                //  service(sock, clientip, clientport);

                // v2版，多进程版本
                // pid_t id = fork();
                // if (id < 0)
                // {
                //     close(sock);
                //     continue;
                // }
                // else if (id == 0) // 子进程，父进程的fd，会被子进程继承吗？会。 父子会用同一张文件描述符表吗？不会，但是子进程会拷贝父进程的fd table
                // {
                //     // 建议关闭掉不需要的fd,因为子进程总有运行结束的一天，会误读，子进程要的是sock
                //     close(listensock_);
                //     if (fork() > 0)
                //         exit(0);
                //     // 走到下面时，子进程已经退了，孙子进程在运行，退出后立马回收，孙子进程就没有父进程，就变成孤儿进程，就会被系统领养
                //     // 孙子进程就给我们提供IO服务，不推荐
                //     //
                //     service(sock, clientip, clientport); // 这个回收，就由系统承担了
                //     exit(0);
                // }
                // else
                // {
                // }

                // // 父,一定要关闭掉不需要的fd，因为一旦获取新连接，这个sock文件描述符被打开了，之后，把这个fork交给子进程，子进程帮你处理了，
                // // 而父进程没有关闭，会让可用资源越来越少，就会导致父进程的文件描述符资源浪费的问题，文件描述符（fd）泄漏
                // close(sock);
                // // waitpid可以以非阻塞方式就等待waitpid(id,nullptr,WNOHANG)，不推进
                // pid_t ret = waitpid(id, nullptr, 0); // 要等待子进程，不等待，可能会导致子进程僵尸之后，无法回收，进而导致内存泄漏
                // // 等待方式为0
                // if (ret == id)
                //     std::cout << "wait child " << id << "success " << std::endl;

                // v3版本，多线程--原生多线程
                // 1.要不要关闭不要的socket？多线程中，文件描述符表都是共享的，不需要，直接可以被外部所看到，关了，会影响我服务器
                // 2.要不要回收线程？如何回收，会不会阻塞？要回收，可以分离，分离后，主线程不用关闭新线程了,主线程可以回过去继续获取连接
                // pthread_t tid;
                // ThreadData *td = new ThreadData(sock, clientip, clientport,this);
                // pthread_create(&tid, nullptr, threadRoutine, td);

                // v4:一旦用户来了，你才创建线程,线程池，线程池中已经封装线程了，可以用单例
                // 使用线程池的时候，一定是有限的线程个数，一定是要处理短任务
                Task t(sock, clientip, clientport, std::bind(&TcpServer::service, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                ThreadPool<Task>::getinstance()->pushTask(t);
            }
        }

        static void *threadRoutine(void *args)
        {
            pthread_detach(pthread_self()); // 分离后，主线程不用关闭新线程了,主线程可以回过去继续获取连接

            ThreadData *td = static_cast<ThreadData *>(args);
            td->current->service(td->sock, td->clientip, td->clientport);
            delete td;
        }

        void service(int sock, const std::string &clientip, const uint16_t &clientport)
        {
            std::string who = clientip + "-" + std::to_string(clientport);
            char buffer[1024];

            // v2,只执行一次任务，发完消息就结束
            ssize_t s = read(sock, buffer, sizeof(buffer) - 1); // 读数据,大于0，读取成功
            if (s > 0)
            {
                buffer[s] = 0;
                std::string res = func_(buffer); // 读到数据以后，回调一下，回调出去，再回调回来，把结果给我，就可以进行特定的处理
                // std::cout << who << ">>> " << res << std::endl;
                logMessage(Debug, "%s# %s", who.c_str(), res.c_str());

                write(sock, res.c_str(), res.size()); // 对对方的客户端进行写回的操作
            }
            else if (s == 0)
            {
                // 对方把连接关闭了

                // std::cout << who << " quit,me too " << std::endl;
                logMessage(Info, "%s quit, me too", who.c_str());
            }
            else
            {

                // std::cout << "read error: " << strerror(errno) << std::endl;
                logMessage(Error, "read error, %d:%s", errno, strerror(errno));
            }
            close(sock);

            // v1
            //  while (true)
            //  {
            //      ssize_t s = read(sock, buffer, sizeof(buffer) - 1); // 读数据,大于0，读取成功
            //      if (s > 0)
            //      {
            //          buffer[s] = 0;
            //          std::string res = func_(buffer); // 读到数据以后，回调一下，回调出去，再回调回来，把结果给我，就可以进行特定的处理
            //          std::cout << who << ">>> " << res << std::endl;

            //         write(sock, res.c_str(), res.size()); // 对对方的客户端进行写回的操作
            //     }
            //     else if (s == 0)
            //     {
            //         // 对方把连接关闭了
            //         close(sock);
            //         std::cout << who << " quit,me too " << std::endl;
            //         break;
            //     }
            //     else
            //     {
            //         close(sock);
            //         std::cout << "read error: " << strerror(errno) << std::endl;
            //         break;
            //     }
            // }
        }
        ~TcpServer()
        {
        }

    private:
        uint16_t port_;
        int listensock_;

        bool quit_; // 表示服务器是否启动
        func_t func_;
    };
}